﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Jiubang.Communication.Core;
using Jiubang.Communication.Server.Buffers;
using Jiubang.Communication.Server.DataTransport;
using Jiubang.Communication.Server.RequestRouter;
using Jiubang.Util;

namespace Jiubang.Communication.Server.SessionControl {
    /// <summary>
    ///   封装<see cref = "SocketServer" />并实现长连接的会话管理逻辑。
    /// 目前版本只考虑一个Socket关联一个群发组的情况。
    /// </summary>
    public sealed class SessionServer : IDisposable, IMessageSender, IMessageListener, IBroadcastable {
        private readonly SocketServer _socketServer;
        private readonly Buffers.BufferManager _bufferManager;
        private readonly IDictionary<ushort, LinkedList<Socket>> _groups;
        private readonly Dictionary<EndPoint, ushort> _sessions;
        private readonly ReaderWriterLockSlim _lock;
        private readonly ObjectPool<ShareBufferCounter> _shareBufferPool;

        #region public members

        /// <summary>
        /// 接收到消息时触发的事件
        /// </summary>
        public event EventHandler<SocketAsyncEventArgs> MessageReceived;

        /// <summary>
        /// 消息发送完毕或由Server克隆缓冲区后的回调。
        /// 由于共享缓冲区管理机制存在，可能缓冲区在该事件触发前已经被清空。
        /// </summary>
        public event EventHandler<SocketAsyncEventArgs> SendBufferReleased;

        /// <summary>
        ///   发送消息头及正文
        /// </summary>
        /// <param name = "socket">发送目标</param>
        /// <param name = "buffer">
        ///   发送的消息正文缓冲区。
        ///   如Offset所在位置足够消息头填充则直接向缓冲区添加消息头声明。
        ///   否则将从bufferManager申请足够长度的byte数组用于并复制缓冲区。
        /// </param>
        /// <returns>是否异步发送</returns>
        public bool SendAsync(Socket socket, ArraySegment<byte> buffer) {
            return _socketServer.SendAsync(socket, buffer);
        }

        /// <summary>
        ///  向多个客户端广播缓冲区的内容
        /// </summary>
        /// <param name="socket">当前客户端的连接。当发送范围包括Current时该值不能为Null</param>
        /// <param name="arraySegment">待发送的缓冲区</param>
        /// <param name="option">发送范围</param>
        public bool Broadcast(Socket socket, ArraySegment<byte> arraySegment, BroadcastOption option) {
            if (option == BroadcastOption.Current) {
                if (socket == null)
                    throw new ArgumentNullException("socket");

                return SendAsync(socket, arraySegment);
            }

            var shareBuffer = _shareBufferPool.Get();
            shareBuffer.Set(1);

            if ((option & BroadcastOption.Current) != BroadcastOption.Current) {
                _socketServer.SendAsync(socket, arraySegment, shareBuffer);
            }

            var groupNo = BitConverter.ToUInt16(arraySegment.Array, arraySegment.Offset);
            if ((option & BroadcastOption.Internal) == BroadcastOption.Internal) {
                LinkedList<Socket> members = _groups[groupNo];
                if (members == null)
                    throw new Exception("指定了一个不存在的群组编号：" + arraySegment.Array.Concat(","));

                lock (members) {
                    foreach (var member in members.Where(member => member != socket)) {
                        _socketServer.SendAsync(member, arraySegment, shareBuffer);
                    }
                }
            }

            if ((option & BroadcastOption.Extenal) == BroadcastOption.Extenal) {
                _lock.EnterReadLock();
                try {
                    foreach (var members in from i in _groups where i.Key != groupNo select i.Value) {
                        lock (members) {
                            foreach (var member in members) {
                                _socketServer.SendAsync(member, arraySegment, shareBuffer);
                            }
                        }
                    }
                } finally {
                    _lock.ExitReadLock();
                }
            }

            /**
             * 如果运行到此shareBuffer的值已经变为－1，
             * 则意味所有的SendCompleted事件都已经完成了回收判断。
             * 需要在下文回收buffer及shareBuffer。
             */
            if (shareBuffer.Release() == -1) {
                _bufferManager.Return(arraySegment.Array);
                _shareBufferPool.Set(shareBuffer);
            }

            return false;
        }

        #endregion

        /// <summary>
        /// </summary>
        public SessionServer(string instanceName, EndPoint localEndPoint, Buffers.BufferManager bufferManager) {
            _socketServer = new SocketServer(instanceName, localEndPoint, bufferManager);
            _bufferManager = bufferManager;

            _sessions = new Dictionary<EndPoint, ushort>(503);
            _groups = new SortedDictionary<ushort, LinkedList<Socket>>();
            _lock = new ReaderWriterLockSlim();
            _shareBufferPool = new ObjectPool<ShareBufferCounter>(97, () => new ShareBufferCounter());

            _socketServer.SocketAccepted += OnSocketAccepted;
            _socketServer.MessageReceived += OnMessageReceived;
            _socketServer.MessageReceived += MessageReceived;
            _socketServer.SendBufferReleased += OnSendingBufferReleased;
            _socketServer.SendBufferReleased += SendBufferReleased;
            _socketServer.SocketClosed += OnSocketClosed;
        }

        private void OnSocketClosed(object sender, SocketAsyncEventArgs e) {
            _lock.EnterUpgradeableReadLock();
            try {
                ushort groupNumber;
                if (_sessions.TryGetValue(e.AcceptSocket.RemoteEndPoint, out groupNumber)) {
                    _lock.EnterWriteLock();
                    try {
                        _sessions.Remove(e.AcceptSocket.RemoteEndPoint);

                        _groups[groupNumber].Remove(e.AcceptSocket);
                    } finally {
                        _lock.ExitWriteLock();
                    }
                }
            } finally {
                _lock.ExitUpgradeableReadLock();
            }
        }

        private void OnSendingBufferReleased(object sender, SocketAsyncEventArgs e) {
            var shareBuffer = e.UserToken as ShareBufferCounter;
            if (shareBuffer != null) {
                if (shareBuffer.Release() == -1) {
                    _bufferManager.Return(e.Buffer);
                    _shareBufferPool.Set(shareBuffer);
                }
                e.UserToken = null;

                /** 
                 * 需要清空SendingBuffer以免被其它事件回调回收Buffer
                 */
                e.SetBuffer(null, 0, 0);
            }
        }

        /// <summary>
        /// 检查是否需要注册Socket信息。
        /// 重置缓冲区正文长度。
        /// </summary>
        private void OnMessageReceived(object sender, SocketAsyncEventArgs e) {
            var groups = e.UserToken as Socket;
            if (groups != null) {
                var groupNumber = BitConverter.ToUInt16(e.Buffer, e.Offset);
                if (groupNumber != 0) {
                    LinkedList<Socket> members;

                    _lock.EnterWriteLock();
                    try {
                        _sessions.Add(e.AcceptSocket.RemoteEndPoint, groupNumber);

                        if (!_groups.TryGetValue(groupNumber, out members)) {
                            members = new LinkedList<Socket>();
                            _groups.Add(groupNumber, members);
                        }
                    } finally {
                        _lock.ExitWriteLock();
                    }

                    lock (members) {
                        members.AddLast(e.AcceptSocket);
                    }
                }
            }

            e.SetBuffer(e.Offset + 2 + 4, e.Count - 2 - 4);
        }

        /// <summary>
        /// Socket被Accept时，
        /// 将SocketAsyncEventArgs的UserToken属性设置为空列表。
        /// </summary>
        static void OnSocketAccepted(object sender, SocketAsyncEventArgs e) {
            e.UserToken = e.AcceptSocket;
        }

        /// <summary>
        /// 结束Server
        /// </summary>
        public void Dispose() {
            _socketServer.Dispose();
        }
    }
}